(function() {
    // 定义一个关于雷的构造函数(传的参数包括行数,列数,以及雷的数量)
    function Mine(tr, td, mineNum) {
        this.tr = tr;
        this.td = td;
        this.mineNum = mineNum;
        this.tds = []; //存储每个单元格的位置信息,是一个二维数组,用于后期地雷和数字的显示
        this.grid = []; //单元格存储的内容信息
        this.surplus = mineNum; //剩余的雷数
        this.allRight = false; //点击的小红旗,判断是否全是雷
        this.parent = document.getElementById('gameArea'); //获取父级元素
    }
    // 初始化界面
    Mine.prototype.init = function() {
        // 渲染tr和td
        this.gameInterface();
        // 获取每个方格具体的类型(雷或者数字)
        this.matter();
        // 调用雷周围更新的最新数据
        this.updateNum();
        // 取消点击鼠标右键的默认事件
        this.parent.oncontextmenu = function() {
            return false;
        }

        //剩余雷数
        this.surplusMine = document.getElementById('surplus');
        this.surplusMine.innerHTML = this.surplus;

    };
    //生成游戏界面所需要的Dom(tabl、tr、td)
    Mine.prototype.gameInterface = function() {
        var _this = this;
        //创建table
        const table = document.createElement('table');
        // 循环遍历tr和td,动态创建tr和td(外层循环管的是行,内层循环管的是列)
        for (let i = 0; i < this.tr; i++) {
            //创建tr,确定行数
            const trDom = document.createElement('tr');
            this.tds[i] = [];
            for (let j = 0; j < this.td; j++) {
                // 创建td,确定列数
                var tdDom = document.createElement('td');
                // tdDom.innerHTML = 0;
                // 将所有创建的td添加到数组中
                this.tds[i][j] = tdDom;
                //把这个格子对应的行与列存到格子身上，为了下面通过这个值去数组里面取到对应的数据；
                tdDom.pos = [i, j];
                //鼠标点击后触发的游戏事件
                tdDom.onmousedown = function() {
                    //这里的this指向的是,触发的事件源.
                    //而_this则是实例对象
                    _this.play(event, this);
                };
                // 将td标签添加到tr中
                trDom.appendChild(tdDom);
            }
            // 将tr标签添加到table中
            table.appendChild(trDom);
        }
        // 将创建的table标签添加到游戏区域
        this.parent.innerHTML = ''; //在渲染不同大小的界面时,应该先清除掉以前的界面
        this.parent.appendChild(table);

    };
    // 生成n个不重复的随机数,主要用于匹配雷
    Mine.prototype.getRandomNum = function() {
        // 生成一个有长度空的数组
        let num = new Array(this.tr * this.td);
        // 遍历数组添加内容到数组
        for (let i = 0; i < num.length; i++) {
            num[i] = i
        }
        //实现生成的数据随机排序
        num.sort(() => {
            return 0.5 - Math.random(0, 1)
        });
        // 截取数据(以雷的数量为准)
        return num.splice(0, this.mineNum)
    };
    //确定每一个方格存储的内容是地雷还是数字
    Mine.prototype.matter = function() {
        //获得一个随机生成数的一个数组,来表示含雷的数组
        let rn = this.getRandomNum();
        console.log(rn);
        let n = 0; //找到格子所对应的索引
        // 循环每一个格子,
        for (let i = 0; i < this.tr; i++) {
            this.grid[i] = [];
            for (let j = 0; j < this.td; j++) {
                //每完成一次循环得到一个新的索引值
                if (rn.indexOf(++n) != -1) {
                    //该条件满足,说明循环到的这个索引在雷的数组里面找到了,说明该索引对应是个雷
                    this.grid[i][j] = {
                        //获得一个方块要用行和列去取,但是要获得该方块周围的位置,则得通过坐标来获取
                        //行列形式 (0,0) (0,1) (0,2)
                        //坐标形式 (0,0) (1,0)  (2,0)  //就说明行列的位置与坐标位置是正好相反的
                        type: 'mine',
                        x: j, //用x,y存储该方格的坐标位置
                        y: i,
                    }
                } else {
                    this.grid[i][j] = {
                        type: 'number',
                        x: j,
                        y: i,
                        value: 0
                    }
                };
            };
        };
    };
    //找到格子周围的另外八个格子
    Mine.prototype.getOther = function(grid) {
        let x = grid.x;
        let y = grid.y;
        let result = []; //把找到格子的坐标返回出去
        // 一个数的周围
        //x-1,y-1    x,y-1   x+1,y-1
        // x-1,y     x,y     x+1,y
        // x-1,y+1,  x,y+1   x+1,y+1
        for (let i = x - 1; i <= x + 1; i++) { //这里是和坐标有关的循环
            for (let j = y - 1; j <= y + 1; j++) {
                // 如果在这几种情况下,就不用找周围格子了
                if (
                    i < 0 || //格子超出左边范围
                    j < 0 || //格子超出上面范围
                    i > this.td - 1 || //格子超出右边
                    j > this.tr - 1 || //格子超出下边范围
                    this.grid[j][i].type == 'mine' || //周围的格子是雷的条件
                    (i == x && j == y) //当前循环到的格子是自己
                ) {
                    continue;
                };
                result.push([j, i]) //要以行列的形式返回出去,因为要通过它取数组的数据
            };
        }
        return result;
    };

    //更新所有雷周围的数字，把它变成对应正确的数字
    Mine.prototype.updateNum = function() {
        for (var i = 0; i < this.tr; i++) {
            for (var j = 0; j < this.td; j++) {
                // 更新的数字是雷周围的数字
                if (this.grid[i][j].type == 'number') {
                    // 当判断格子类型为数字时,数字不会改变,直接跳出本次循环
                    continue;
                } else {
                    //获取到雷周围的数字的集合
                    var num = this.getOther(this.grid[i][j]);
                    for (var k = 0; k < num.length; k++) {
                        this.grid[num[k][0]][num[k][1]].value += 1;
                    }

                }
            }
        }
    };

    // 游戏开始
    Mine.prototype.play = function(e, obj) {
        var _this = this;
        // 点击左键的触发的事件
        if (e.which == 1 && obj.className != 'flag') {
            // 获取到被点击格子的相关信息
            let currentGrid = this.grid[obj.pos[0]][obj.pos[1]];
            // console.log(currentGrid);
            var bc = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight'];
            if (currentGrid.type == 'number') {
                // 如果点击的格子下方的type:number,显示对应的数字
                obj.innerText = currentGrid.value;
                // 根据对应的数字,添加相应的类名,显示不同的颜色
                obj.className = bc[currentGrid.value];
                if (currentGrid.value == 0) {
                    // 当点击数字为0时,就会打开周围为0的格子,直到遇到雷和不为0的数字(但是遇到的数字要显示出来)
                    //1.找到为0的地方,内容显示为空
                    obj.innerText = '';
                    // 2.找到当前点击为0的格子周围的八个格子,判断是否有为0的格子,如果有,又继续以新的这个0点开始寻找,如果没有就停止查找,以此类推
                    function zeroSearch(grid) {
                        //得到点击方格周围的8个位置
                        var around = _this.getOther(grid);
                        // console.log(around);
                        for (var i = 0; i < around.length; i++) {
                            // 拿到8个格子对应的行、列位置,
                            var x = around[i][0];
                            var y = around[i][1];
                            _this.tds[x][y].className = bc[_this.grid[x][y].value];
                            if (_this.grid[x][y].value == 0) {
                                // 如果有为0的,在找以他为中心的另8个位置(使用递归)
                                if (!_this.tds[x][y].check) {
                                    // 给这个格子添加一个属性,若没有该属性就添加为true
                                    // 确保找过的格子不再反复查找
                                    _this.tds[x][y].check = true;
                                    zeroSearch(_this.grid[x][y])
                                }
                            } else {
                                // 如果找到以0为中心的四周格子不是0,那就把他四周的数字显示出来
                                _this.tds[x][y].innerText = _this.grid[x][y].value;
                            }
                        }
                    }
                    zeroSearch(currentGrid);
                }
            } else {
                this.gameOver(obj);
            }
        };
        // 点击鼠标右键触发的事件(小红旗的添加和移除,表示用户判断地雷的有无)
        if (e.which == 3) {
            // 如果该方块是数字,则该方块不能被点击
            if (obj.className && obj.className != 'flag') {
                return;
            }
            //点击添加/取消flag的类名
            obj.className = obj.className == 'flag' ? '' : 'flag';

            if (this.grid[obj.pos[0]][obj.pos[1]].type == 'mine') {
                this.allRight = true; //用户标的全是雷
            } else {
                this.allRight = false;
            }
            //当类名为flag时,剩余数减1
            if (obj.className == 'flag') {
                this.surplusMine.innerHTML = --this.surplus;
            } else {
                this.surplusMine.innerHTML = ++this.surplus;
            }
            // 当剩余数为0时,判断雷是否全部选中
            if (this.surplus == 0) {
                if (this.allRight) {
                    if (confirm('你太棒啦,游戏通过')) {
                        mine.init()
                    }
                } else {
                    this.gameOver(obj);
                }
            }
        }
    };
    // 游戏失败
    Mine.prototype.gameOver = function(obj) {
        // 1.点击到是雷的位置时,全部的雷显示出来
        for (let i = 0; i < this.tr; i++) {
            for (let j = 0; j < this.td; j++) {
                // 当格子类型为雷时
                if (this.grid[i][j].type == 'mine') {
                    // 当前格子满足类名为flag,且含有地雷,则显示小红旗图标,表示选择正确
                    if (this.tds[i][j].className == 'flag') {
                        this.tds[i][j].className = 'flag';
                    } else {
                        this.tds[i][j].className = 'bomb';
                    }
                    // 当格子类型为数字时
                } else {
                    // 然后格子又有flag类名时,表明没有选中地雷,显示小叉
                    if (this.tds[i][j].className == 'flag') {
                        this.tds[i][j].className = 'error';
                    }
                }

                //2.禁用点击事件
                this.tds[i][j].onmousedown = null;
            }
        }
        // 3.将目标雷的背景颜色改为红色
        obj.style.backgroundColor = 'red';
        // alert('游戏结束')
    };
    window.Mine = Mine
})();

(function() {
    let arr = [
        [10, 10, 9],
        [16, 16, 40],
        [28, 28, 99]
    ];
    let btns = document.getElementsByTagName('button');
    for (let i = 0; i < btns.length - 1; i++) {
        btns[i].onclick = function() {
            for (let j = 0; j < btns.length - 1; j++) {
                btns[j].className = '';
            }
            this.className = 'active';
            let mine = new Mine(...arr[i]);
            mine.init();
            btns[3].onclick = function() {
                mine = new Mine(...arr[i]);
                mine.init();
            }

        }

    }
    btns[0].click();
})()